package services

import models.Colegio
import enums.Jornada
import models.CalendarioAcademico
import models.Periodo
import java.util.Calendar
import java.util.Date
import util.EPDate
import models.Materia
import models.Grado
import models.Curso
import models.MateriaGrado
import models.MateriaCurso
import models.Persona
import enums.TipoDocumento
import enums.Sexo
import models.Profesor
import scala.util.Random
import actors.UserCreator
import models.users.User
import util.StringUtil
import enums.Role
import scala.collection.JavaConversions
import models.Estudiante
import models.Coordinador
import models.Entity
import models.Matricula
import models.Acudiente
import models.Colegio
import enums.EPState
import models.Indicador
import enums.CategoriaIndicador
import scala.io.Source

object DemoCreator {
  val nombresTuplas = Source.fromFile("private/data/nombres.txt").getLines.map(linea => { val split = linea.split("="); (split(0), split(1)) }).toList
  val nombresMujer = nombresTuplas.filter(t => t._2.equals("MUJER"))
  val nombresHombre = nombresTuplas.filter(t => t._2.equals("HOMBRE"))
  val nombresTodos = Map("MUJER" -> nombresMujer, "HOMBRE" -> nombresHombre)

  val apellidos = Source.fromFile("private/data/apellidos.txt").getLines.map(s => s).toList

  val random = new Random(System.currentTimeMillis())

  def crearColegioTemplate(nombreColegio: String, prefijoColegio: String) {

    val colegio = BD create new Colegio {
      setNombre(nombreColegio)
      setDireccion(randomAddress())
      setJornada(Jornada.MANANA)
      setTelefono(randomPhone())
      setState(EPState.SUBIDA_NOTAS)
    }
    val inicioCalendario = EPDate("2014/02/05")
    val finCalendario = EPDate("2014/10/30")

    //id_calendario_academico, fecha_inicio,fecha_fin, numero
    var inicioPeriodo = inicioCalendario

    //descripcion,id_colegio,fecha_inicio, fecha_fin, num_periodos
    val calendarioAcademico = BD create new CalendarioAcademico {
      setDescripcion("2014")
      setIdColegio(colegio.getId())
      setFechaInicio(inicioCalendario.date)
      setFechaFin(finCalendario.date)
      setNumPeriodos(4)
      //setPeriodos(JavaConversions.seqAsJavaList(periodos))
    }

    val periodos =
      for (i <- 1 to 4) yield BD create new Periodo {
        inicioPeriodo = inicioPeriodo plus 2 days;
        setFechaIncio(inicioPeriodo.date)
        inicioPeriodo = inicioPeriodo plus 2 months;
        setFechaFin(inicioPeriodo.date)
        setNumero(i)
        setCalendarioAcademico(calendarioAcademico)
      }

    colegio.setIdCalendarioAcademicoActual(calendarioAcademico.getId())
    colegio.setIdPeriodoActual(periodos(0).getId())
    BD update colegio

    //INSERT INTO materia (nombre, descripcion, id_colegio)
    val mats = List("Matemáticas", "Español", "Física", "Sociales", "Inglés", "Educación Física", "Ética y Valores")
    val materias =
      for (m <- mats) yield BD create new Materia {
        setNombre(m)
        setDescripcion(m)
        setIdColegio(colegio.getId())
      }

    //INSERT INTO grado (nombre,prefijo,  id_colegio)
    val grads = List("Primero", "Segundo", "Tercero", "Cuarto", "Quinto", "Sexto", "Septimo", "Octavo", "Noveno", "Décimo", "Once")
    val grados =
      for (g <- 0 to 10) yield BD create new Grado {
        setNombre(grads(g))
        setPrefijo(g + 1 + "")
        setIdColegio(colegio.getId())
      }

    val materiasGrado =
      for (m <- materias; g <- grados) yield BD create new MateriaGrado {
        setIdColegio(colegio.getId())
        setIdGrado(g.getId())
        setIdMateria(m.getId())
      }

    val indicadores =
      for (mg <- materiasGrado; p <- periodos; c <- CategoriaIndicador.values) yield BD create new Indicador {
        setConsecutivo(1)
        setDescripcion(c match { case CategoriaIndicador.Fortalezas => "Muy bien" case CategoriaIndicador.Debilidades => "Medio mal" case _ => "Se puede mejorar" })
        setIdGrado(mg.getIdGrado())
        setIdMateria(mg.getIdMateria())
        setIdPeriodo(p.getId())
        setCategoria(c)
      }

    //insert into curso (nombre, id_calendario_academico,id_grado)
    val cursos =
      for (g <- grados) yield BD create new Curso {
        setNombre(g.getPrefijo() + "01")
        setIdCalendarioAcademico(calendarioAcademico.getId())
        setIdGrado(g.getId())
      }

    //Profesores
    //insert into persona(nombres,apellidos,email, documento, tipo_documento, sexo, fecha_nacimiento, direccion)
    val profBaseDate = EPDate(System.currentTimeMillis()) minus 45 years;
    val numProf = 10;
    val personasProf = for (i <- 0 to numProf - 1) yield BD create new Persona {
      val documento = prefijoColegio + "Prof" + i

      val name = randomName

      setNombres(name._1)
      setApellidos(randomLastname)
      setEmail(s"$documento@$prefijoColegio.com")
      setDocumento(documento)
      setTipoDocumento(TipoDocumento.CC)
      setSexo(name._2)
      setFechaNacimiento((profBaseDate plus random.nextInt(20) years).date)
      setDireccion(randomAddress)
      setTelefono(randomPhone)
    }

    val profesores =
      for (p <- personasProf) yield BD create new Profesor {
        setPersona(p)
        setIdColegio(colegio.getId())
      }

    val usuariosProf =
      for (p <- profesores) yield BD create new User {
        setUsername(p.getPersona().getDocumento())
        setPassword(PasswordHash.createHash(p.getPersona().getDocumento()))
        setId(p.getPersona().getId())
      }

    val materiasCurso =
      for (m <- materias; c <- cursos) yield BD create new MateriaCurso {
        setIdCurso(c.getId())
        setIdMateria(m.getId())
        setProfesor(profesores(random.nextInt(numProf)))
      }

    val numEst = 14
    val matriculas = cursos.map(c =>
      {
        val estBaseDate = EPDate(System.currentTimeMillis()) minus (7 + cursos.indexOf(c)) years;
        val acuBaseDate = EPDate(System.currentTimeMillis()) minus 45 years;
        val mas = for (i <- 0 to numEst + random.nextInt(3)) yield {
          val dir = randomAddress()
          val phone = randomPhone()
          val last = randomLastname
          val p = BD create new Persona {
            val name = randomName
            val documento = prefijoColegio + c.getNombre() + "Est" + i
            setNombres(name._1)
            setApellidos(last)
            setEmail(s"$documento@$prefijoColegio.com")
            setDocumento(documento)
            setTipoDocumento(TipoDocumento.NUIP)
            setSexo(name._2)
            setFechaNacimiento((estBaseDate plus (random.nextInt(3) - 1) years).date)
            setDireccion(dir)
            setTelefono(phone)
          }
          val pa = BD create new Persona {
            val documento = prefijoColegio + c.getNombre() + "Acu" + i
            val name = randomName
            setNombres(name._1)
            setApellidos(last)
            setEmail(s"$documento@$prefijoColegio.com")
            setDocumento(documento)
            setTipoDocumento(TipoDocumento.CC)
            setSexo(name._2)
            setFechaNacimiento((acuBaseDate plus random.nextInt(20) years).date)
            setDireccion(dir)
            setTelefono(phone)
          }
          val a = BD create new Acudiente {
            setPersona(pa)
          }
          val usuarioAcu =
            BD create new User {

              setUsername(a.getPersona().getDocumento())
              setPassword(PasswordHash.createHash(a.getPersona().getDocumento()))
              setId(a.getPersona.getId)
            }

          val e = BD create new Estudiante {
            setPersona(p)
            setIdColegio(colegio.getId())
            setIdGrado(c.getIdGrado())
            setIdAcudiente(a.getId())
          }
          var m: Matricula = null
          if (random.nextInt(10) < 8)
            m = BD create new Matricula {
              setIdCalendarioAcademico(calendarioAcademico.getId())
              setIdCurso(c.getId())
              setIdEstudiante(e.getId())
            }
          m
        }
        mas.filter(_ != null)
      })

    val personaCoor = BD create new Persona {
      val name = randomName
      setNombres(name._1)
      setApellidos(randomLastname)
      setEmail(s"coor@$prefijoColegio.com")
      setDocumento(prefijoColegio + "Coor")
      setTipoDocumento(TipoDocumento.CC)
      setSexo(name._2)
      setFechaNacimiento((profBaseDate plus 2 years).date)
      setDireccion(randomAddress)
    }

    val coordinador =
      BD create new Coordinador {
        setPersona(personaCoor)
        setIdColegio(colegio.getId())
      }

    val usuarioCoor =
      BD create new User {

        setUsername(coordinador.getPersona().getDocumento())
        setPassword(PasswordHash.createHash(coordinador.getPersona().getDocumento()))
        setId(coordinador.getPersona.getId)
      }

  }
  var test = false;
  def main(args: Array[String]) {
    test = true
    crearColegioTemplate("test0", "t0")
  }

  object BD {
    var id = 1;

    def prepare[T <: Entity[_]](o: T): T = {
      persist(o, false)
    }

    def create[T <: Entity[_]](o: T): T = {
      persist(o, true)
    }

    def update[T <: Entity[_]](o: T): T = {
      if (!test) {
        DAO.persist(o)
        DAO.flush()
      }
      o
    }

    private def persist[T <: Entity[_], R](o: T, flush: Boolean): R = {

      val c = o.getClass()
      val ec = o.getClass().getSuperclass()
      val ins = ec.newInstance()

      c.getMethods().foreach(m => {
        if (m.getName().startsWith("get") && m.getParameterTypes().length == 0 && m.getDeclaringClass().getSuperclass() == classOf[Entity[_]]) {
          val v = m.invoke(o)
          val setter = ec.getMethod(m.getName().replace("get", "set"), m.getReturnType())
          setter.invoke(ins, v)
        }
      })

      if (test)
        try {
          val co = o.asInstanceOf[Entity[Long]]
          val cins = ins.asInstanceOf[Entity[Long]]
          co.setId(id)
          cins.setId(id)
          id += 1;
        } catch {
          case x: Exception =>
        }
      else {
        DAO.persist(ins);
        if (flush)
          DAO.flush();
      }

      println(s"${ins.getClass().getSimpleName()}($ins)")
      ins.asInstanceOf[R]
    }

  }

  private def randomAddress(): String = {
    s"${if (random.nextBoolean) "Carrera" else "Calle"} ${random.nextInt(100) + 1} # ${random.nextInt(100) + 1} - ${random.nextInt(100) + 1}"
  }

  private def randomPhone(): String = {
    (1 to 7).map(x => random.nextInt(10)).mkString
  }

  private def randomName(): (String, Sexo) = {
    val n = nombresTuplas(random.nextInt(nombresTuplas.size))
    val n1 = n._1
    val otrosNombres = nombresTodos(n._2)
    val n2 = if (random.nextBoolean) " " + otrosNombres(random.nextInt(otrosNombres.size))._1 else ""
    (n1 + n2, Sexo.valueOf(n._2))
  }

  private def randomLastname(): String = {
    val a1 = apellidos(random.nextInt(apellidos.size))
    val a2 = if (random.nextBoolean) " " + apellidos(random.nextInt(apellidos.size)) else ""
    a1 + a2
  }

  private def randomFullName(): (String, Sexo) = {
    val name = randomName
    (name._1 + randomLastname, name._2)
  }

}